using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Roslyn.Compilers;
using Roslyn.Compilers.CSharp;
using SymbolicComputation;
namespace Sym.Test
{
    public class RandomEquationGenerator
    {
        static Random random = new Random();
        public enum enumBinaryKinds { Add, Subtract, Multiply, Divide }
        public enum enumExpressionType { NoEqualSign, EqualSign }
        List<SyntaxKind> binaryKinds = CreateBinaryKinds();

        Func<SyntaxNode> CreateNodeFunction;
        public String CreateExpression(int layers, enumExpressionType expressionType)
        {
            CreateNodeFunction = RandomPowerNode;
            //CreateNodeFunction = RandomBinaryAndParanthesisNode;
            //CreateNodeFunction = RandomNode;
            int layerIndex;
            SyntaxNode root = null;
            List<SyntaxNode> branches;
        StartOver:

            root = CreateNodeFunction();
            for (layerIndex = 1; layerIndex < layers; layerIndex++)
            {
                branches = ChildStringBranches(root);
                root = Miscellaneous.ReplaceNodes(root, branches, x => RandomNode(x));
            }
            branches = ChildStringBranches(root);
            root = Miscellaneous.ReplaceNodes(root, branches, x => RandomNumericNode(x,-2,4));
            if (expressionType == enumExpressionType.EqualSign)
            {
                SyntaxNode equationRoot = EquationNode();
                Double val = RoslynEval.ScriptEval.Evaluate(root.ToString());
                if (Double.IsNaN(val) == true)
                {
                    goto StartOver;
                }
                if (Double.IsNegativeInfinity(val) == true)
                {
                    goto StartOver;
                }
                if (Double.IsPositiveInfinity(val) == true)
                {
                    goto StartOver;
                }
                if ( val > 10000 | val<-10000 | val == 0 | val == 1 )
                {
                    goto StartOver;
                }
                SyntaxNode lhs = Miscellaneous.EquationToSyntaxNode(val.ToString() + "d");
                branches = equationRoot.ChildNodes().ToList();
                equationRoot = equationRoot.ReplaceNode(branches[0], lhs);
                branches = equationRoot.ChildNodes().ToList();
                equationRoot = equationRoot.ReplaceNode(branches[1], root);
                return equationRoot.ToString();
            }
            return root.ToString();
        }

        public SyntaxNode RandomNode(SyntaxNode oldNode)
        {
            return Transform.WrapNodeInBrackets(oldNode.Parent, CreateNodeFunction());
        }

        public SyntaxNode RandomNumericNode(SyntaxNode oldNode,Double lBound,Double range)
        {
            return Transform.WrapNodeInBrackets(oldNode.Parent, RandomNumericNode(lBound,range));
        }

        public static List<SyntaxNode> ChildStringBranches(SyntaxNode root)
        {
            List<SyntaxNode> branches = root.DescendentNodesAndSelf().ToList();
            var childBranches = from n in branches
                                 where n.ToString() == "child"
                                 select n;
            return childBranches.ToList();
        }
        public SyntaxNode RandomNumericNode(Double lBound,Double range)
        {
            String eq = Convert.ToString(random.NextDouble() * range + lBound)+"d";
            //String eq = Convert.ToString(random.NextDouble() * range) + "d";
            SyntaxNode sn = Miscellaneous.EquationToSyntaxNode(eq);
            return sn;
        }

        public SyntaxNode RandomBinaryAndParanthesisNode()
        {
            double num = random.NextDouble();
            if (num > 0 & num <= .75)
            {
                return RandomBinaryNode();
            }
            else if (num > .75 & num <= 1)
            {
                return ParanthesesNode();
            }
            return null;
        }

        public SyntaxNode RandomPowerNode()
        {
            double num = random.NextDouble();
            if (num > 0 & num <= .7)
            {
                return RandomBinaryNode();
            }
            else if (num > .7 & num <= .8)
            {
                return ParanthesesNode();
            }
            else if (num > .1 & num <= 1)
            {
                return RandomPowerNode(2);
            }
            return null;
        }

        public SyntaxNode RandomNode()
        {
            double num = random.NextDouble();
            if (num >0 & num <=.5)
            {
                return RandomBinaryNode();
            }
            else if (num > .5 & num <= .6)
            {
                return ParanthesesNode();
            }
            else if (num > .6 & num <= .7)
            {
                return RandomPowerNode(2);
            }
            else if (num > .7 & num <= 1)
            {
                return RandomTrigonometryNode(1);
            }
            return null;
        }

        public SyntaxNode RandomBinaryNode()
        {
            int num = random.Next(4);  //fix divisions
            return CreateBinaryNode(binaryKinds[num]);
        }

        public static SyntaxNode ParanthesesNode()
        {
            return Miscellaneous.EquationToSyntaxNode("(child)");
        }

        public static SyntaxNode RandomPowerNode(double range)
        {
            return Miscellaneous.EquationToSyntaxNode("Math.Pow(child,child);");
        }

        public static SyntaxNode RandomTrigonometryNode(double range)
        {
            String[] trigNames = { "Sin", "Cos", "Tan", "Asin", "Acos", "Atan" };
            int trigNameIndex = random.Next(6);
            String trigName = "Math." + trigNames[trigNameIndex];
            SyntaxNode lOut = Miscellaneous.EquationToSyntaxNode(trigName + "(child);");
            return lOut;
        }

        public static List<SyntaxKind> CreateBinaryKinds()
        {
            List<SyntaxKind> lOut = new List<SyntaxKind>();
            lOut.Add(SyntaxKind.AddExpression);
            lOut.Add(SyntaxKind.SubtractExpression);
            lOut.Add(SyntaxKind.MultiplyExpression);
            lOut.Add(SyntaxKind.DivideExpression);
            return lOut;
        }

        private static SyntaxNode CreateBinaryNode(SyntaxKind inKind)
        {
            ExpressionSyntax left = Syntax.IdentifierName("child");
            ExpressionSyntax right = Syntax.IdentifierName("child");
            SyntaxToken st = new SyntaxToken();
            return Syntax.BinaryExpression(inKind, left, st, right);
        }

        public static SyntaxNode EquationNode()
        {
            return Miscellaneous.EquationToSyntaxNode("child==child");
        }
    }
}